
#include "xmodule_config.h"

#if MODULE_CHECK_ENABLED( BATTERY )

#include "rtl876x.h"
#include "rtl876x_gpio.h"
#include "rtl876x_pinmux.h"
#include "rtl876x_rcc.h"
#include "rtl876x_adc.h"
#include "rtl876x_nvic.h"
//#include "bee2_adc_lib.h"
#include "os_timer.h"

#include "em_battery.h"
#include "xDLPS.h"

// #include "battery_curve\battery_curve_sample.h"
#if BATTERY_CURVE_HOOK_ENABLE == 0 
#include BATTERY_CURVE_FILE
#endif 
#if ADC_MULTIPLE_CHANNEL
#include ADC_MULTI_CHANNELS_HEADER_FILE
#endif

#include "boards.h"

#if MODULE_CHECK_LOG_ENABLED( BATTERY )
#include "log.h"
#define LOG     LOG_INFO
#define LOG0    LOG_INFO0 
#define LOG1    LOG_INFO1
#define LOG2    LOG_INFO2
#define LOG3    LOG_INFO3
#define FMT_STR FORMAT_STRING
#else 
#define LOG(...) 
#define LOG0(...)
#define LOG1(...)
#define LOG2(...)
#define LOG3(...)
#define FMT_STR(...)
#endif 

#define STATUS_NO_CHARGE        0 
#define STATUS_IN_CHARGE        1

static em_battery_evt_callback_t m_local_cb = NULL; 
static bool m_local_timer_is_started = false; 
void* batteryTimer = NULL;

static uint8_t status_of_charge;
static uint8_t charge_cnt;

static bool adc_ft_flag = false;
//static uint8_t battery_dlps_index;

static const T_IO_MODULE_CBS battery_cbs = 
{
    .init = NULL,
    .enter_dlps = em_battery_enter_dlps,
    .exit_dlps = em_battery_exit_dlps,
    .check_allow_dlps = NULL, 
    .system_wakeup_check_allow_dlps = em_battery_system_wakeup_dlps_check, 
    .name = "battery\0",
};

///////////////////////////////////////////////////////////////////////////////////
static void set_pin_int_mode(uint8_t pin , GPIOIT_PolarityType mode)
{
    GPIO_InitTypeDef gpioInitStruct;
    GPIO_StructInit( &gpioInitStruct );
    
    gpioInitStruct.GPIO_Pin             = GPIO_GetPin( pin );
    gpioInitStruct.GPIO_Mode            = GPIO_Mode_IN;
    gpioInitStruct.GPIO_ITCmd           = ENABLE;
    gpioInitStruct.GPIO_ITTrigger       = GPIO_INT_Trigger_EDGE;
    gpioInitStruct.GPIO_ITPolarity      = mode;
	gpioInitStruct.GPIO_ITDebounce      = GPIO_INT_DEBOUNCE_ENABLE;
	gpioInitStruct.GPIO_DebounceTime    = 1;
    GPIO_Init( &gpioInitStruct );
}

static void set_pin_int_enable(uint8_t pin , FunctionalState en)
{
    GPIO_INTConfig( GPIO_GetPin( pin ), en );
    GPIO_MaskINTConfig( GPIO_GetPin( pin ), en==ENABLE ? DISABLE : ENABLE);
    if(en==ENABLE)GPIO_ClearINTPendingBit(GPIO_GetPin(pin));
}


void BATTERY_CHARGE_DETECT_IRQ_HANDLER(void)
{
    LOG0("=battery irq=\r\n");

    set_pin_int_enable(BATTERY_CHARGE_DETECT_PIN,DISABLE);
    
    if( GPIO_ReadInputDataBit( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN) ) > 0 ) 
    {
		set_pin_int_mode(BATTERY_CHARGE_DETECT_PIN,GPIO_INT_POLARITY_ACTIVE_LOW);
    }
    else 
    {
		set_pin_int_mode(BATTERY_CHARGE_DETECT_PIN,GPIO_INT_POLARITY_ACTIVE_HIGH);
    }

    if( m_local_timer_is_started == false ) 
    {
        m_local_timer_is_started = true;
        os_timer_start( &batteryTimer );
        LOG0("[BATTERY]: timer started\r\n");
    }

    set_pin_int_enable(BATTERY_CHARGE_DETECT_PIN,ENABLE);
}
static void battery_timer_timeout_handler( void* xTimer )
{
    LOG1("[%s]: timer timeout \r\n", FMT_STR( __FUNCTION__ ) );
    
    if( status_of_charge == STATUS_NO_CHARGE )
    {
        // check charger  
        if(  GPIO_ReadInputDataBit( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN) ) > 0 )
        {
            charge_cnt ++;
            if( charge_cnt > BATTERY_CHARGE_DETECT_CONFIRM_TIMES ) 
            {
                charge_cnt = 0;
                status_of_charge = STATUS_IN_CHARGE;
                os_timer_stop( &batteryTimer );
                m_local_timer_is_started = false;
                if( m_local_cb != NULL )
                {
                    em_battery_evt_t evt;
                    evt.type = EM_BATTERY_EVT_CHARGE_DETECT;
                    m_local_cb( &evt ); 
                }
            }
        }
        else 
        {
            charge_cnt ++;
            if( charge_cnt > BATTERY_CHARGE_REMOVE_CONFIRM_TIMES ) 
            {
                charge_cnt = 0;
                status_of_charge = STATUS_NO_CHARGE;
                os_timer_stop( &batteryTimer );
                m_local_timer_is_started = false;
            }
        }
    }
    else 
    {
        if(  GPIO_ReadInputDataBit( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN) ) == 0 ) 
        {
            charge_cnt ++;
            if( charge_cnt > BATTERY_CHARGE_REMOVE_CONFIRM_TIMES ) 
            {
                charge_cnt = 0;
                status_of_charge = STATUS_NO_CHARGE; 
                
                os_timer_stop( &batteryTimer );
                m_local_timer_is_started = false; 
                
                if( m_local_cb != NULL )
                {
                    em_battery_evt_t evt;
                    evt.type = EM_BATTERY_EVT_CHARGE_REMOVE;
                    m_local_cb( &evt ); 
                }
            }
        }
        else 
        {
            charge_cnt ++;
            if( charge_cnt > BATTERY_CHARGE_DETECT_CONFIRM_TIMES ) 
            {
                charge_cnt = 0;
                status_of_charge = STATUS_IN_CHARGE; 
                os_timer_stop( &batteryTimer );
                m_local_timer_is_started = false; 
            }
        }
    }
}

static void config_gpios(void)
{
    Pad_Config( BATTERY_ADC_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    Pinmux_Config( BATTERY_ADC_PIN, IDLE_MODE ); 
    
#if ADC_MULTIPLE_CHANNEL
    ADC_MULTI_CHANNEL_CONFIG();
#else 
    ADC_DeInit( ADC ); 
    RCC_PeriphClockCmd( APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE ); 
    
    ADC_InitTypeDef adcInitStruct;
    ADC_StructInit( &adcInitStruct ); 
    for( uint8_t i = 0; i < 16; i++)
    {
        adcInitStruct.schIndex[i] = EXT_SINGLE_ENDED( BATTERY_ADC_INDEX ); 
    }
    adcInitStruct.bitmap = 0xFFFF;
    adcInitStruct.adcSamplePeriod = 14; // 0.5us 
    ADC_Init( ADC, &adcInitStruct ); 
    ADC_INTConfig( ADC, ADC_INT_ONE_SHOT_DONE, ENABLE );    
#endif

    adc_ft_flag = ADC_CalibrationInit();
    // Charge detect pin 
    Pad_Config( BATTERY_CHARGE_DETECT_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW);
    Pinmux_Config( BATTERY_CHARGE_DETECT_PIN, DWGPIO ); 
    GPIO_InitTypeDef gpioInitStruct;
    GPIO_StructInit( &gpioInitStruct ); 
    gpioInitStruct.GPIO_Pin         = GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN );
    gpioInitStruct.GPIO_Mode        = GPIO_Mode_IN;
    gpioInitStruct.GPIO_ITCmd       = ENABLE;
    gpioInitStruct.GPIO_ITPolarity  = GPIO_INT_POLARITY_ACTIVE_HIGH;
    gpioInitStruct.GPIO_ITTrigger   = GPIO_INT_Trigger_EDGE;
    gpioInitStruct.GPIO_DebounceTime= 1;
    GPIO_Init( &gpioInitStruct ); 
    
    GPIO_INTConfig( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN ), ENABLE ); 
    GPIO_MaskINTConfig( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN), DISABLE ); 
    System_WakeUpPinEnable( BATTERY_CHARGE_DETECT_PIN, PAD_WAKEUP_POL_HIGH, PAD_WK_DEBOUNCE_DISABLE ); 
    
    NVIC_InitTypeDef nvicInitStruct;
    nvicInitStruct.NVIC_IRQChannel          = BATTERY_CHARGE_DETECT_IRQn;
    nvicInitStruct.NVIC_IRQChannelPriority  = 3;
    nvicInitStruct.NVIC_IRQChannelCmd       = ENABLE;
    NVIC_Init( &nvicInitStruct ); 
}

void em_battery_enter_dlps(void)
{
    Pad_Config( BATTERY_ADC_PIN, PAD_SW_MODE, PAD_NOT_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    
    Pad_Config( BATTERY_CHARGE_DETECT_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
#ifdef BATTERY_CHARGE_FULL_PIN
    Pad_Config( BATTERY_CHARGE_FULL_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
#endif
}
void em_battery_exit_dlps(void)
{
    Pad_Config( BATTERY_ADC_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    
    Pad_Config( BATTERY_CHARGE_DETECT_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW );
#ifdef BATTERY_CHARGE_FULL_PIN
    Pad_Config( BATTERY_CHARGE_FULL_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
#endif 
}
bool em_battery_system_wakeup_dlps_check(void)
{
    if( System_WakeUpInterruptValue( BATTERY_CHARGE_DETECT_PIN) == SET 
#ifdef BATTERY_CHARGE_FULL_PIN
    ||  System_WakeUpInterruptValue( BATTERY_CHARGE_FULL_PIN ) == SET 
#endif 
    )
    {
        Pad_ClearWakeupINTPendingBit( BATTERY_CHARGE_DETECT_PIN ); 
#ifdef BATTERY_CHARGE_FULL_PIN
        Pad_ClearWakeupINTPendingBit( BATTERY_CHARGE_FULL_PIN ); 
#endif 
        return true;
    }
    return false; 
}

void em_battery_init(void)
{
    config_gpios();
    
    charge_cnt = 0;
    if( GPIO_ReadInputDataBit( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN) )  > 0 ) 
    {
        status_of_charge = STATUS_IN_CHARGE;
    }
    else 
    {
        status_of_charge = STATUS_NO_CHARGE;
    }

    if( batteryTimer == NULL ) 
    {
        os_timer_create( &batteryTimer, "batt_timer", 0, BATTERY_INTERVAL_OF_CHARGE_DETECT_TIMER, true, battery_timer_timeout_handler );
        if(batteryTimer && status_of_charge == STATUS_IN_CHARGE)
        {
        m_local_timer_is_started = true;
        os_timer_start( &batteryTimer );
        }
    }
    

}

void em_battery_init_dlps(void)
{
    /*battery_dlps_index = */
    uint32_t index = xDLPS_register( &battery_cbs );
    LOG1("[BATTERY]: DLPS index = %d\r\n", index );
}

uint32_t em_battery_get_voltage(void)
{
    uint16_t result;
    #if ADC_MULTIPLE_CHANNEL
    uint16_t d[2];
    ADC_MULTI_CHANNEL_CONVERT( d );
    result = d[0];
    #else 
    ADC_BypassCmd( BATTERY_ADC_INDEX, ENABLE ); 
    ADC_Cmd( ADC, ADC_One_Shot_Mode, ENABLE ); 
    while( ADC_GetIntFlagStatus( ADC, ADC_INT_ONE_SHOT_DONE) != SET );
    result = ADC_ReadByScheduleIndex( ADC, 0 ); //15
    ADC_Cmd( ADC, ADC_One_Shot_Mode, DISABLE ); 
    ADC_ClearINTPendingBit( ADC, ADC_INT_ONE_SHOT_DONE ); 
    #endif
    uint32_t volt;
    if( adc_ft_flag ) 
    {
        float direct_voltage = 0; 
        
        ADC_ErrorStatus error_status = NO_ERROR;
        direct_voltage = ADC_GetVoltage( BYPASS_SINGLE_MODE, result, &error_status ); 
        uint32_t battery_value = direct_voltage;
        
        battery_value = (float)1220 / 220 * direct_voltage;       
        volt = battery_value;
    }
    else 
    {
        uint16_t voltage = 0.2511 * result - 32.7247; // y = kx + b, R1 = 100K, R2 = 30K
        voltage = (float)1220 / 220 * voltage;
        volt = voltage;
    }    
    
     
    return volt;
}

void em_battery_register_callback( em_battery_evt_callback_t cb )
{
    m_local_cb = cb; 
}

#if BATTERY_CURVE_HOOK_ENABLE == 0 

uint8_t em_battery_calc_level_normal(uint16_t volt)
{
    for(int i = 0; i < sizeof(battery_voltage_table) / sizeof(battery_voltage_table[0]); i++)
    {
        if( volt >= battery_voltage_table[i][0] )
        {
            return battery_voltage_table[i][1];            
        }
    }
    return 0;
}
uint8_t em_battery_calc_level_under_charging(uint16_t volt)
{
    for(int i = 0; i < sizeof(battery_voltage_incharing_table) / sizeof(battery_voltage_incharing_table[0]); i++)
    {
        if( volt >= battery_voltage_incharing_table[i][0] )
        {
            return battery_voltage_incharing_table[i][1];            
        }
    }
    return 0;
}
#endif // !  BATTERY_CURVE_HOOK_ENABLE == 0 

#if BATTERY_CURVE_HOOK_ENABLE 
uint8_t em_battery_calc_level_normal(uint16_t volt)
{
    return BATTERY_CURVE_HOOK_COMPUTE_LEVEL_NORMAL( volt );
}
uint8_t em_battery_calc_level_under_charging(uint16_t volt)
{
    return BATTERY_CURVE_HOOL_COMPUTE_LEVEL_CHARGING( volt );
}
#endif // !BATTERY_CURVE_HOOK_ENABLE

bool em_battery_check_charger_plug_in(void)
{
    // return STATUS_NO_CHARGE != status_of_charge;// 
    return GPIO_ReadInputDataBit( GPIO_GetPin( BATTERY_CHARGE_DETECT_PIN) ) > 0;
}


#endif 
